package cn.org.xwb.main.aspect;

import cn.org.xwb.main.annotation.mail.MailLegalAnno;
import cn.org.xwb.main.annotation.mail.MailLegalObjAnno;
import cn.org.xwb.main.annotation.mail.MailLegalParamAnno;
import cn.org.xwb.main.constants.ResponseConstant;
import cn.org.xwb.main.dto.ClientErrorRespResult;
import cn.org.xwb.main.utils.AspectUtil;
import cn.org.xwb.main.utils.MailUtil;
import cn.org.xwb.main.utils.ReflectUtil;
import lombok.extern.log4j.Log4j2;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.Arrays;

/**
 * 判断邮箱是否合法的切面类
 */
@Aspect
@Order(4)
@Component
@Log4j2
public class MailLegalAspect {

    /**
     * 和邮箱相关的工具类
     */
    @Autowired
    private MailUtil mailUtil;

    @Pointcut("@annotation(cn.org.xwb.main.annotation.mail.MailLegalAnno)")
    private void pointCut() {
    }

    /**
     * 判断邮箱是否合法
     *
     * @param proceedingJoinPoint 切点
     * @return 响应结果对象
     */
    @Around("pointCut()")
    public Object judgeMailIsLegalAroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        // 对需要进行判断邮箱是否的合法的参数进行判断处理
        Object[] results = AspectUtil.doParamHandleBeforeAdvice(
                proceedingJoinPoint,
                MailLegalObjAnno.class,
                (Object arg) -> {
                    // 判断对象形式参数中被 MailLegalObjAnno 标注的字段邮箱是否合法
                    return judgeMailIsLegalObjField(arg);
                },
                MailLegalParamAnno.class,
                (Object arg) -> {
                    // 判断当前参数 邮箱是否合法
                    return mailUtil.mailIsLegal((String) arg);
                }
        );
        // 存在不合法的邮箱，返回错误响应结果对象
        if (Arrays.stream(results).anyMatch(result -> (Boolean) result == false)) {
            log.error("方法 {} 接收到的邮箱不合法", AspectUtil.getPointMethodSignatureStr(proceedingJoinPoint));
            return ClientErrorRespResult.clientErrorRespResult(ResponseConstant.MAIL_IS_ILLEGAL);
        }
        // 执行切点方法并返回切点方法的执行结果
        return proceedingJoinPoint.proceed();
    }

    /**
     * 判断对象中被 MailLegalAnnotation 标注的字段的邮箱
     * 是否合法
     *
     * @param object 对象
     * @return 合法 true；不合法 false
     */
    private Boolean judgeMailIsLegalObjField(Object object) {
        try {
            ReflectUtil.doObjAllFieldHandleByAnnotation(
                    object,
                    (Object obj, Field field, Annotation[] annotations) -> {
                        // 当前字段没有被 MailLegalAnnotation 标注直接判断下一个字段
                        if (!ReflectUtil.judgeContainAnnotation(annotations, MailLegalAnno.class)) {
                            return;
                        }
                        // 当前字段被 MailLegalAnnotation 标注，需要进行判断
                        // 获取当前字段的值
                        String fieldValue = null;
                        fieldValue = (String) ReflectUtil.getFieldValue(obj, field);
                        // 如果邮箱不合法，抛出错误
                        if (!mailUtil.mailIsLegal(fieldValue)) {
                            throw new RuntimeException();
                        }
                    }
            );
        } catch (Exception e) {
            // 捕获到因邮箱不合法抛出的错误
            return false;
        }
        // 邮箱合法
        return true;
    }

}
